using Sirenix.Utilities;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public static class ListUtil
{

    public static List<TTo> ConvertElementType<TTo>(this IList list)
    {
        var ret = new List<TTo>();
        foreach (var one in list)
        {
            var e = (TTo)(object)one;
            ret.Add(e);
        }
        return ret;
    }

    public static T TryGet<T>(List<T> list, int index, T @default = default)
    {
        if (list == null)
        {
            return @default;
        }
        if (index < 0 || index >= list.Count)
        {
            return @default;
        }
        return list[index];
    }

    public static List<TOut> ChangeElementType<TIn, TOut>(List<TIn> list)
    {
        var result = new List<TOut>();
        foreach (var item in list)
        {
            result.Add((TOut)(object)item);
        }
        return result;
    }

    public static List<object> ToObjectList<T>(List<T> list)
    {
        var ret = ChangeElementType<T, object>(list);
        return ret;
    }

    /// <summary>
    /// 获取子列表
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <param name="startIndex">开始的下标</param>
    /// <param name="length">截取个数，null 表示直到结尾</param>
    /// <returns></returns>
    public static List<T> Sublist<T>(List<T> list, int startIndex, int? length)
    {
        var ret = new List<T>();
        var count = 0;
        for (int i = startIndex; i < list.Count; i++)
        {
            if (length != null)
            {
                if (count >= length)
                {
                    break;
                }
            }
            var one = list[i];
            ret.Add(one);
            count++;
        }
        return ret;
    }



    public static void RemoveCommonElements<T>(List<T> list1, List<T> list2)
    {
        if (list1 == null || list2 == null)
        {
            return;
        }
        for (int i = list1.Count - 1; i >= 0; i--)
        {
            if (list2.Contains(list1[i]))
            {
                list2.Remove(list1[i]);
                list1.RemoveAt(i);
            }
        }
    }

    public static T GetNearestValidElement<T>(List<T> list, int index)
    {
        if (index < 0)
        {
            index = 0;
        }
        if (index >= list.Count)
        {
            index = list.Count - 1;
        }
        return list[index];
    }

    public static bool IsElementEqual<T>(List<T> listA, List<T> listB)
    {
        if (listA.Count != listB.Count)
        {
            return false;
        }
        var set = new HashSet<T>();
        set.AddRange(listA);

        foreach (var element in listB)
        {
            var b = set.Contains(element);
            if (!b)
            {
                return false;
            }
        }
        return true;
    }

    public static T GetRandomElement<T>(List<T> list, T _default = default)
    {
        if(list.Count == 0)
        {
            return _default;
        }
        var index = UnityEngine.Random.Range(0, list.Count);
        return list[index];
    }


    /// <summary>
    /// 随机两个不同元素，但如果输入数组只有1个元素，将放回相同元素
    /// </summary>
    /// <returns></returns>
    public static (T, T) GetRandomTwoElement<T>(List<T> list)
    {
        if (list.Count == 1)
        {
            var ret = list[0];
            return (ret, ret);
        }
        var indexA = UnityEngine.Random.Range(0, list.Count);
        var indexB = UnityEngine.Random.Range(0, list.Count - 1);
        if (indexB >= indexA)
        {
            indexB += 1;
        }
        var a = list[indexA];
        var b = list[indexB];
        return (a, b);
    }

    /// <summary>
    /// 随机多个不同的元素，如果备选的元素不够，只会返回备选元素数量
    /// 例：2 个里选 3 个，只会返回 2 个
    /// </summary>
    /// <returns></returns>
    public static List<T> GetRandomMultiDiffrentElement<T>(List<T> list, int randomTimes)
    {
        if (list.Count <= randomTimes)
        {
            return list;
        }

        var elementList = new List<T>(list);
        var ret = new List<T>();

        for(int i = 0; i < randomTimes; i++)
        {
            var index = UnityEngine.Random.Range(0, elementList.Count);
            var element = elementList[index];
            ret.Add(element);
            elementList.RemoveAt(index);
        }
        return ret;
    }

    public static T GetRandomByWight<T>(List<T> list) where T : IWeightable
    {
        var totalWeight = 0;
        foreach(var one in list)
        {
            totalWeight += one.Wight;
        }
        var randomValue = UnityEngine.Random.Range(0, totalWeight);
        var floor = 0;
        foreach(var one in list)
        {
            var ceil = floor + one.Wight;
            if(randomValue < ceil)
            {
                return one;
            }
            floor = ceil;
        }
        throw new Exception("should not reach here");
    }

    public static List<T> FindLasted<T>(IList<T> list, int count)
    {
        var ret = new List<T>();
        var startIndex = list.Count - count;
        if (startIndex < 0)
        {
            startIndex = 0;
        }
        for (int i = startIndex; i < list.Count; i++)
        {
            var info = list[i];
            ret.Add(info);
        }
        return ret;
    }

    public static T TryGetFirst<T>(List<T> list, T _default = default)
    {
        if (list.Count == 0)
        {
            return _default;
        }
        else
        {
            return list[0];
        }
    }
}

public interface IWeightable
{
    public int Wight { get; }
}
